//
//  SYHTTPWorking.m
//  SYBaseSDK_Example
//
//  Created by ligq on 2020/3/26.
//  Copyright © 2020 sysjones. All rights reserved.
//

#import "SYHTTPWorking.h"
#import "SYSDKKit.h"
#import "AFNetworkActivityIndicatorManager.h"


static NSString *sg_privateNetworkBaseUrl = nil;
static BOOL sg_isEnableInterfaceDebug = NO;
static BOOL sg_shouldAutoEncode = NO;
static NSDictionary *sg_httpHeaders = nil;
static SYResponseType sg_responseType = kSYResponseTypeJSON;
static SYRequestType  sg_requestType  = kSYRequestTypePlainText;
static SYNetworkStatus sg_networkStatus = kSYNetworkStatusReachableViaWiFi;
static NSMutableArray *sg_requestTasks;
//static BOOL sg_cacheGet = YES;
//static BOOL sg_cachePost = NO;
static BOOL sg_shouldCallbackOnCancelRequest = YES;
static NSTimeInterval sg_timeout = 60.0f;
//static BOOL sg_shoulObtainLocalWhenUnconnected = NO;
static BOOL sg_isBaseURLChanged = YES;
static AFHTTPSessionManager *sg_sharedManager = nil;
//static NSUInteger sg_maxCacheSize = 0;

@implementation SYHTTPWorking

+ (void)updateBaseUrl:(NSString *)baseUrl {
  if (![baseUrl isEqualToString:sg_privateNetworkBaseUrl] && baseUrl && baseUrl.length) {
    sg_isBaseURLChanged = YES;
  } else {
    sg_isBaseURLChanged = NO;
  }
  
  sg_privateNetworkBaseUrl = baseUrl;
}

+ (NSString *)baseUrl {
  return sg_privateNetworkBaseUrl;
}

+ (void)setTimeout:(NSTimeInterval)timeout {
  sg_timeout = timeout;
}

+ (void)enableInterfaceDebug:(BOOL)isDebug {
  sg_isEnableInterfaceDebug = isDebug;
}

+ (BOOL)isDebug {
  return sg_isEnableInterfaceDebug;
}
+ (void)configRequestType:(SYRequestType)requestType
             responseType:(SYResponseType)responseType
      shouldAutoEncodeUrl:(BOOL)shouldAutoEncode
  callbackOnCancelRequest:(BOOL)shouldCallbackOnCancelRequest {
  sg_requestType = requestType;
  sg_responseType = responseType;
  sg_shouldAutoEncode = shouldAutoEncode;
  sg_shouldCallbackOnCancelRequest = shouldCallbackOnCancelRequest;
}

+ (BOOL)shouldEncode {
  return sg_shouldAutoEncode;
}
+ (void)configCommonHttpHeaders:(NSDictionary *)httpHeaders {
  sg_httpHeaders = httpHeaders;
}

#pragma mark - Private
+ (AFHTTPSessionManager *)manager {
  @synchronized (self) {
    // 只要不切换baseurl，就一直使用同一个session manager
    if (sg_sharedManager == nil || sg_isBaseURLChanged) {
      // 开启转圈圈
      [AFNetworkActivityIndicatorManager sharedManager].enabled = YES;
      AFHTTPSessionManager *manager = nil;;
      if ([self baseUrl] != nil) {
        manager = [[AFHTTPSessionManager alloc] initWithBaseURL:[NSURL URLWithString:[self baseUrl]]];
      } else {
        manager = [AFHTTPSessionManager manager];
      }
      
      switch (sg_requestType) {
        case kSYRequestTypeJSON: {
          manager.requestSerializer = [AFJSONRequestSerializer serializer];
          break;
        }
        case kSYRequestTypePlainText: {
          manager.requestSerializer = [AFHTTPRequestSerializer serializer];
          break;
        }
        default: {
          break;
        }
      }
      
      switch (sg_responseType) {
        case kSYResponseTypeJSON: {
          manager.responseSerializer = [AFJSONResponseSerializer serializer];
          break;
        }
        case kSYResponseTypeXML: {
          manager.responseSerializer = [AFXMLParserResponseSerializer serializer];
          break;
        }
        case kSYResponseTypeData: {
          manager.responseSerializer = [AFHTTPResponseSerializer serializer];
          break;
        }
        default: {
          break;
        }
      }
      
      manager.requestSerializer.stringEncoding = NSUTF8StringEncoding;
      for (NSString *key in sg_httpHeaders.allKeys) {
        if (sg_httpHeaders[key] != nil) {
          [manager.requestSerializer setValue:sg_httpHeaders[key] forHTTPHeaderField:key];
        }
      }
      
      manager.responseSerializer.acceptableContentTypes = [NSSet setWithArray:@[@"application/json",
                                                                                @"text/html",
                                                                                @"text/json",
                                                                                @"text/plain",
                                                                                @"text/javascript",
                                                                                @"text/xml",
                                                                                @"image/*"]];
      
      manager.requestSerializer.timeoutInterval = sg_timeout;
      
      // 设置允许同时最大并发数量，过大容易出问题
      manager.operationQueue.maxConcurrentOperationCount = 3;
      sg_sharedManager = manager;
    }
  }
  
  return sg_sharedManager;
}
//适配HTTPS
+ (void)configHTTPSService{
    sg_sharedManager.securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeNone];;
    sg_sharedManager.securityPolicy.allowInvalidCertificates = YES;
    [sg_sharedManager.securityPolicy setValidatesDomainName:NO];
}

//检测网络
+ (void)detectNetwork {
  AFNetworkReachabilityManager *reachabilityManager = [AFNetworkReachabilityManager sharedManager];
  [reachabilityManager startMonitoring];
  [reachabilityManager setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
    if (status == AFNetworkReachabilityStatusNotReachable){
      sg_networkStatus = kSYNetworkStatusNotReachable;
      SYAppLog(@"无网络");
    } else if (status == AFNetworkReachabilityStatusUnknown){
      sg_networkStatus = kSYNetworkStatusUnknown;
      SYAppLog(@"未知");
    } else if (status == AFNetworkReachabilityStatusReachableViaWWAN){
      sg_networkStatus = kSYNetworkStatusReachableViaWWAN;
      SYAppLog(@"流量");
    } else if (status == AFNetworkReachabilityStatusReachableViaWiFi){
      sg_networkStatus = kSYNetworkStatusReachableViaWiFi;
      SYAppLog(@"WI-FI");
    }
  }];
}

+ (RACSignal *)getWithUrl:(NSString *)url
                  success:(SYResponseSuccess)success
                     fail:(SYResponseFail)fail{
    return [self getWithUrl:url
                     params:nil
                    success:success
                       fail:fail];
}

// 多一个params参数
+ (RACSignal *)getWithUrl:(NSString *)url
                   params:(NSDictionary *)params
                  success:(SYResponseSuccess)success
                     fail:(SYResponseFail)fail{
    return [self getWithUrl:url
                     params:params
                   progress:nil
                    success:success
                       fail:fail];
}
// 多一个带进度回调
+ (RACSignal *)getWithUrl:(NSString *)url
                   params:(NSDictionary *)params
                 progress:(SYGetProgress)progress
                  success:(SYResponseSuccess)success
                     fail:(SYResponseFail)fail{
    return [self _requestWithUrl:url
                       httpMedth:kSYHTTPMethodTypeGet
                          params:params progress:progress
                         success:success
                            fail:fail];
}

+ (RACSignal *)postWithUrl:(NSString *)url
                            params:(NSDictionary *)params
                           success:(SYResponseSuccess)success
                      fail:(SYResponseFail)fail{
    return [self postWithUrl:url
                      params:params
                    progress:nil
                     success:success
                        fail:fail];
}

+ (RACSignal *)postWithUrl:(NSString *)url
                            params:(NSDictionary *)params
                          progress:(SYPostProgress)progress
                           success:(SYResponseSuccess)success
                      fail:(SYResponseFail)fail{
    return [self _requestWithUrl:url
                       httpMedth:kSYHTTPMethodTypePost
                          params:params
                        progress:progress
                         success:success
                            fail:fail];
}

+ (RACSignal *)_requestWithUrl:(NSString *)url
                     httpMedth:(SYHTTPMethodType)httpMethod
                        params:(NSDictionary *)params
                      progress:(SYDownloadProgress)progress
                       success:(SYResponseSuccess)success
                          fail:(SYResponseFail)fail{
    if ([self shouldEncode]) {
      url = [self encodeUrl:url];
    }
    AFHTTPSessionManager *manager = [self manager];
    NSString *absolute = [self absoluteUrlWithPath:url];
    
    if ([self baseUrl] == nil) {
      if ([NSURL URLWithString:url] == nil) {
        SYAppLog(@"URLString无效，无法生成URL。可能是URL中有中文，请尝试Encode URL");
        return nil;
      }
    } else {
      NSURL *absoluteURL = [NSURL URLWithString:absolute];
      
      if (absoluteURL == nil) {
        SYAppLog(@"URLString无效，无法生成URL。可能是URL中有中文，请尝试Encode URL");
        return nil;
      }
    }
    @weakify(self)
    return  [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
        @strongify(self)
        if (httpMethod == kSYHTTPMethodTypeGet) {
            [manager GET:absolute parameters:params headers:nil progress:^(NSProgress * _Nonnull downloadProgress) {
                if (progress) {
                  progress(downloadProgress.completedUnitCount, downloadProgress.totalUnitCount);
                }
            } success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
                [self successResponse:responseObject callback:success];
                if ([self isDebug]) {
                    [self logWithSuccessResponse:responseObject
                                           url:absolute
                                        params:params];
                }
                [subscriber sendNext:responseObject];
                [subscriber sendCompleted];

            } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
                [self handleCallbackWithError:error fail:fail];
                 if ([self isDebug]) {
                   [self logWithFailError:error url:absolute params:params];
                 }
                [subscriber sendError:error];
            }];
        }else if (httpMethod == kSYHTTPMethodTypePost){
            [manager POST:absolute parameters:params headers:nil progress:^(NSProgress * _Nonnull uploadProgress) {
                if (progress) {
                  progress(uploadProgress.completedUnitCount, uploadProgress.totalUnitCount);
                }
            } success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
                [self successResponse:responseObject callback:success];
                if ([self isDebug]) {
                    [self logWithSuccessResponse:responseObject
                                           url:absolute
                                        params:params];
                }
                 [subscriber sendNext:responseObject];
                 [subscriber sendCompleted];
            } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
                [self handleCallbackWithError:error fail:fail];
                if ([self isDebug]) {
                  [self logWithFailError:error url:absolute params:params];
                }
                [subscriber sendError:error];
            }];
        }
        return [RACDisposable disposableWithBlock:^{
            [manager invalidateSessionCancelingTasks:YES resetSession:NO];
        }];
    }];
    
}
+ (RACSignal *)uploadWithImage:(UIImage *)image
                           url:(NSString *)url
                      filename:(NSString *)filename
                          name:(NSString *)name
                      mimeType:(NSString *)mimeType
                    parameters:(NSDictionary *)parameters
                      progress:(SYUploadProgress)progress
                       success:(SYResponseSuccess)success
                          fail:(SYResponseFail)fail{
    if ([self baseUrl] == nil) {
      if ([NSURL URLWithString:url] == nil) {
        SYAppLog(@"URLString无效，无法生成URL。可能是URL中有中文，请尝试Encode URL");
        return nil;
      }
    } else {
      if ([NSURL URLWithString:[NSString stringWithFormat:@"%@%@", [self baseUrl], url]] == nil) {
        SYAppLog(@"URLString无效，无法生成URL。可能是URL中有中文，请尝试Encode URL");
        return nil;
      }
    }
    
    if ([self shouldEncode]) {
      url = [self encodeUrl:url];
    }
    
    NSString *absolute = [self absoluteUrlWithPath:url];
    
    AFHTTPSessionManager *manager = [self manager];
    @weakify(self);
    return  [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
        @strongify(self);
        [manager POST:absolute parameters:parameters headers:nil constructingBodyWithBlock:^(id<AFMultipartFormData>  _Nonnull formData) {
            NSData *imageData = UIImageJPEGRepresentation(image, 1);
            NSString *imageFileName = filename;
            if (filename == nil || ![filename isKindOfClass:[NSString class]] || filename.length == 0) {
              NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
              formatter.dateFormat = @"yyyyMMddHHmmss";
              NSString *str = [formatter stringFromDate:[NSDate date]];
              imageFileName = [NSString stringWithFormat:@"%@.jpg", str];
            }
            // 上传图片，以文件流的格式
            [formData appendPartWithFileData:imageData name:name fileName:imageFileName mimeType:mimeType];
        } progress:^(NSProgress * _Nonnull uploadProgress) {
            if (progress) {
              progress(uploadProgress.completedUnitCount, uploadProgress.totalUnitCount);
            }
        } success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
            [self successResponse:responseObject callback:success];
            if ([self isDebug]) {
              [self logWithSuccessResponse:responseObject
                                       url:absolute
                                    params:parameters];
            }
        } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
            [self handleCallbackWithError:error fail:fail];
            if ([self isDebug]) {
              [self logWithFailError:error url:absolute params:nil];
            }
        }];
        return [RACDisposable disposableWithBlock:^{
            [manager invalidateSessionCancelingTasks:YES resetSession:NO];
        }];
    }];
}
+ (RACSignal *)uploadFileWithUrl:(NSString *)url
                   uploadingFile:(NSString *)uploadingFile
                        progress:(SYUploadProgress)progress
                         success:(SYResponseSuccess)success
                            fail:(SYResponseFail)fail{
    if ([NSURL URLWithString:uploadingFile] == nil) {
      SYAppLog(@"uploadingFile无效，无法生成URL。请检查待上传文件是否存在");
      return nil;
    }
    
    NSURL *uploadURL = nil;
    if ([self baseUrl] == nil) {
      uploadURL = [NSURL URLWithString:url];
    } else {
      uploadURL = [NSURL URLWithString:[NSString stringWithFormat:@"%@%@", [self baseUrl], url]];
    }
    
    if (uploadURL == nil) {
      SYAppLog(@"URLString无效，无法生成URL。可能是URL中有中文或特殊字符，请尝试Encode URL");
      return nil;
    }
    AFHTTPSessionManager *manager = [self manager];
    NSURLRequest *request = [NSURLRequest requestWithURL:uploadURL];
    @weakify(self)
    return [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
       @strongify(self)
        [manager uploadTaskWithRequest:request fromFile:[NSURL URLWithString:uploadingFile] progress:^(NSProgress * _Nonnull uploadProgress) {
          if (progress) {
            progress(uploadProgress.completedUnitCount, uploadProgress.totalUnitCount);
          }
        } completionHandler:^(NSURLResponse * _Nonnull response, id  _Nullable responseObject, NSError * _Nullable error) {
          [self successResponse:responseObject callback:success];
          if (error) {
            [self handleCallbackWithError:error fail:fail];
            if ([self isDebug]) {
              [self logWithFailError:error url:response.URL.absoluteString params:nil];
            }
          } else {
            if ([self isDebug]) {
              [self logWithSuccessResponse:responseObject
                                       url:response.URL.absoluteString
                                    params:nil];
            }
          }
        }];
        return [RACDisposable disposableWithBlock:^{
            [manager invalidateSessionCancelingTasks:YES resetSession:NO];
        }];
    }];
}

+ (RACSignal *)downloadWithUrl:(NSString *)url
                    saveToPath:(NSString *)saveToPath
                      progress:(SYDownloadProgress)progressBlock
                       success:(SYResponseSuccess)success
                       failure:(SYResponseFail)failure{
    if ([self baseUrl] == nil) {
      if ([NSURL URLWithString:url] == nil) {
        SYAppLog(@"URLString无效，无法生成URL。可能是URL中有中文，请尝试Encode URL");
        return nil;
      }
    } else {
      if ([NSURL URLWithString:[NSString stringWithFormat:@"%@%@", [self baseUrl], url]] == nil) {
        SYAppLog(@"URLString无效，无法生成URL。可能是URL中有中文，请尝试Encode URL");
        return nil;
      }
    }
    
    NSURLRequest *downloadRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:url]];
    AFHTTPSessionManager *manager = [self manager];
    return [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
        [manager downloadTaskWithRequest:downloadRequest progress:^(NSProgress * _Nonnull downloadProgress) {
          if (progressBlock) {
            progressBlock(downloadProgress.completedUnitCount, downloadProgress.totalUnitCount);
          }
        } destination:^NSURL * _Nonnull(NSURL * _Nonnull targetPath, NSURLResponse * _Nonnull response) {
          return [NSURL fileURLWithPath:saveToPath];
        } completionHandler:^(NSURLResponse * _Nonnull response, NSURL * _Nullable filePath, NSError * _Nullable error) {
          if (error == nil) {
            if (success) {
              success(filePath.absoluteString);
            }
            if ([self isDebug]) {
              SYAppLog(@"Download success for url %@",
              [self absoluteUrlWithPath:url]);
            }
          } else {
            [self handleCallbackWithError:error fail:failure];

            if ([self isDebug]) {
              SYAppLog(@"Download fail for url %@, reason : %@",
              [self absoluteUrlWithPath:url],
              [error description]);
            }
          }
        }];
        return [RACDisposable disposableWithBlock:^{
            [manager invalidateSessionCancelingTasks:YES resetSession:NO];
        }];
    }];
}
//打印返回数据字段
+ (void)logWithSuccessResponse:(id)response url:(NSString *)url params:(NSDictionary *)params {
  SYAppLog(@"\n");
  SYAppLog(@"\nRequest success, URL: %@\n params:%@\n response:%@\n\n",
            [self generateGETAbsoluteURL:url params:params],
            params,
            [self tryToParseData:response]);
}

+ (void)logWithFailError:(NSError *)error url:(NSString *)url params:(id)params {
  NSString *format = @" params: ";
  if (params == nil || ![params isKindOfClass:[NSDictionary class]]) {
    format = @"";
    params = @"";
  }
  
  SYAppLog(@"\n");
  if ([error code] == NSURLErrorCancelled) {
    SYAppLog(@"\nRequest was canceled mannully, URL: %@ %@%@\n\n",
              [self generateGETAbsoluteURL:url params:params],
              format,
              params);
  } else {
    SYAppLog(@"\nRequest error, URL: %@ %@%@\n errorInfos:%@\n\n",
              [self generateGETAbsoluteURL:url params:params],
              format,
              params,
              [error localizedDescription]);
  }
}

// 仅对一级字典结构起作用
+ (NSString *)generateGETAbsoluteURL:(NSString *)url params:(id)params {
  if (params == nil || ![params isKindOfClass:[NSDictionary class]] || [params count] == 0) {
    return url;
  }
  
  NSString *queries = @"";
  for (NSString *key in params) {
    id value = [params objectForKey:key];
    
    if ([value isKindOfClass:[NSDictionary class]]) {
      continue;
    } else if ([value isKindOfClass:[NSArray class]]) {
      continue;
    } else if ([value isKindOfClass:[NSSet class]]) {
      continue;
    } else {
      queries = [NSString stringWithFormat:@"%@%@=%@&",
                 (queries.length == 0 ? @"&" : queries),
                 key,
                 value];
    }
  }
  
  if (queries.length > 1) {
    queries = [queries substringToIndex:queries.length - 1];
  }
  
  if (([url hasPrefix:@"http://"] || [url hasPrefix:@"https://"]) && queries.length > 1) {
    if ([url rangeOfString:@"?"].location != NSNotFound
        || [url rangeOfString:@"#"].location != NSNotFound) {
      url = [NSString stringWithFormat:@"%@%@", url, queries];
    } else {
      queries = [queries substringFromIndex:1];
      url = [NSString stringWithFormat:@"%@?%@", url, queries];
    }
  }
  
  return url.length == 0 ? queries : url;
}

+ (NSString *)encodeUrl:(NSString *)url {
  return [self sy_URLEncode:url];
}

+ (id)tryToParseData:(id)responseData {
  if ([responseData isKindOfClass:[NSData class]]) {
    // 尝试解析成JSON
    if (responseData == nil) {
      return responseData;
    } else {
      NSError *error = nil;
      NSDictionary *response = [NSJSONSerialization JSONObjectWithData:responseData
                                                               options:NSJSONReadingMutableContainers
                                                                 error:&error];
      
      if (error != nil) {
        return responseData;
      } else {
        return response;
      }
    }
  } else {
    return responseData;
  }
}

+ (void)successResponse:(id)responseData callback:(SYResponseSuccess)success {
  if (success) {
    success([self tryToParseData:responseData]);
  }
}

+ (NSString *)sy_URLEncode:(NSString *)url {
//  return [url stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
    return [url stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet letterCharacterSet]];
}

+ (NSString *)absoluteUrlWithPath:(NSString *)path {
  if (path == nil || path.length == 0) {
    return @"";
  }
  
  if ([self baseUrl] == nil || [[self baseUrl] length] == 0) {
    return path;
  }
  
  NSString *absoluteUrl = path;
  
  if (![path hasPrefix:@"http://"] && ![path hasPrefix:@"https://"]) {
    if ([[self baseUrl] hasSuffix:@"/"]) {
      if ([path hasPrefix:@"/"]) {
        NSMutableString * mutablePath = [NSMutableString stringWithString:path];
        [mutablePath deleteCharactersInRange:NSMakeRange(0, 1)];
        absoluteUrl = [NSString stringWithFormat:@"%@%@",
                       [self baseUrl], mutablePath];
      } else {
        absoluteUrl = [NSString stringWithFormat:@"%@%@",[self baseUrl], path];
      }
    } else {
      if ([path hasPrefix:@"/"]) {
        absoluteUrl = [NSString stringWithFormat:@"%@%@",[self baseUrl], path];
      } else {
        absoluteUrl = [NSString stringWithFormat:@"%@/%@",
                       [self baseUrl], path];
      }
    }
  }
  
  return absoluteUrl;
}

+ (void)handleCallbackWithError:(NSError *)error fail:(SYResponseFail)fail {
  if ([error code] == NSURLErrorCancelled) {
    if (sg_shouldCallbackOnCancelRequest) {
      if (fail) {
        fail(error);
      }
    }
  } else {
    if (fail) {
      fail(error);
    }
  }
}
@end
